package com.bryanprogramming.zend.gui;

import com.bryanprogramming.zend.logic.Player;
import com.bryanprogramming.zend.logic.ZEnd;
import com.bryanprogramming.zend.logic.testcases.TestCases;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

/**
 *
 * @author charles bryan
 */
public class Game extends JPanel implements PropertyChangeListener, KeyListener {

    private Board board;
    private ZEnd game;
    private JLabel scoreLabel, coinsLabel;
    private int score;
    private final boolean TESTING = false;
    private JButton undoButton, wordButton, powerUpButton;
    private boolean makingWord = false;
    private Player player;
    public final static DecimalFormat FMT = new DecimalFormat("#,###");

    private Game(ZEnd game, Player p) {
        this.addKeyListener(this);
        this.player = p;
        this.player.addPropertyChangeListener(this);

        board = new Board(true);
        this.setLayout(new BorderLayout(0, 10));

        //pane.add(buildScorePanel(), BorderLayout.NORTH);
        this.add(board, BorderLayout.NORTH);
        this.add(buildButtonPanel(), BorderLayout.CENTER);
        this.add(buildStatusPanel(), BorderLayout.SOUTH);

        //allow keylistener to work by giving focus to the frame
        this.setFocusable(true);
        this.requestFocusInWindow();

        this.game = new ZEnd();
        this.game.addPropertyChangeListener(board);
        this.game.addPropertyChangeListener(this);
        if (TESTING) {
            this.game.startGame(TestCases.COLOR_CASE_2);
        }
        else {
            this.game.startGame();
        }
        board.startDrawing();
    }

    public static void createAndShowGUI(ZEnd game, Player p) {
        //Create and set up the window.

        JFrame frame = new JFrame("Z-End");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        JComponent newContentPane = new Game(game, p);
        newContentPane.setOpaque(true); //content panes must be opaque

        frame.setJMenuBar(((Game) newContentPane).buildMenu());
        frame.setContentPane(newContentPane);
        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    private void resetboard() {
        board.reset();
        undoButton.setEnabled(game.getUndoStatus());
        wordButton.setText("Make Word");
        makingWord = false;
    }

    private JPanel buildStatusPanel() {
        JPanel statusPanel = new JPanel(new BorderLayout());
        statusPanel.setBackground(Color.LIGHT_GRAY);
        statusPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.DARK_GRAY));

        Font font = new Font("San-Serif", Font.BOLD, 10);
        FontMetrics fm = this.getFontMetrics(font);
        int width = fm.stringWidth(" SCORE: 999,999");
        int height = fm.getHeight();

        JPanel scorePanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        scorePanel.setBorder(BorderFactory.createMatteBorder(0, 1, 0, 0, Color.GRAY));
        scoreLabel = new JLabel(" SCORE: 0");
        scoreLabel.setPreferredSize(new Dimension(width, height));
        scoreLabel.setFont(font);
        scorePanel.setBackground(Color.LIGHT_GRAY);
        scorePanel.add(scoreLabel);

        statusPanel.add(scorePanel, BorderLayout.EAST);

        JPanel coinsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        coinsPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
        coinsPanel.setBackground(Color.LIGHT_GRAY);

        width = fm.stringWidth("Gold Coins: 1,999,999");
        height = fm.getHeight();

        coinsLabel = new JLabel("Gold Coins: " + FMT.format(this.player.getGoldCoins()));
        coinsLabel.setFont(font);
        coinsLabel.setBackground(Color.LIGHT_GRAY);
        coinsLabel.setPreferredSize(new Dimension(width, height));
        coinsPanel.add(coinsLabel);
        statusPanel.add(coinsPanel, BorderLayout.WEST);

        return statusPanel;
    }

    private JPanel buildButtonPanel() {
        JPanel buttonPanel = new JPanel(new BorderLayout(5, 5));
        undoButton = new JButton("Undo (5)");
        undoButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                game.undo();

                //allow keylistener to work by giving focus back to the frame
                setFocusable(true);
                requestFocusInWindow();
            }
        });
        undoButton.setEnabled(false);
        buttonPanel.add(undoButton, BorderLayout.NORTH);

        wordButton = new JButton("Make Word");
        wordButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (!makingWord) {
                    wordButton.setText("Submit");
                    undoButton.setEnabled(false);
                }
                else {
                    wordButton.setText("Make Word");
                    undoButton.setEnabled(game.getUndoStatus());
                    Point[] p = board.getWordList();
                    game.spellWord(p);
                }
                makingWord = !makingWord;
                board.setInWordMode(makingWord);

                //allow keylistener to work by giving focus back to the frame
                setFocusable(true);
                requestFocusInWindow();
            }
        });
        buttonPanel.add(wordButton, BorderLayout.CENTER);

        this.powerUpButton = new JButton("Use Power Up!");
        powerUpButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {

                javax.swing.SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        UsePowerUpWindow.createAndShowGUI(player, game);
                    }
                });

                //allow keylistener to work by giving focus back to the frame
                setFocusable(true);
                requestFocusInWindow();
            }
        });
        powerUpButton.setEnabled(true);
        buttonPanel.add(powerUpButton, BorderLayout.SOUTH);

        return buttonPanel;
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals(ZEnd.Properties.SCORE_CHANGED)) {
            this.score = (Integer) evt.getNewValue();
            scoreLabel.setText("SCORE: " + FMT.format(this.score));
        }
        else if (evt.getPropertyName().equals(ZEnd.Properties.GAME_STATUS_CHANGED)) {
            if ((boolean) evt.getNewValue()) {
                JOptionPane.showMessageDialog(this, "You Won!\n"
                        + "Final Score: " + this.score);
            }
            else {
                JOptionPane.showMessageDialog(this, "You Lost!\n"
                        + "Final Score: " + this.score);
            }

        }
        else if (evt.getPropertyName().equals(ZEnd.Properties.UNDO_COUNT_CHANGED)) {
            int count = (int) evt.getNewValue();
            undoButton.setEnabled(game.getUndoStatus());
            undoButton.setText("Undo (" + count + ")");
        }
        else if (evt.getPropertyName().equals(ZEnd.Properties.UNDO_STATUS_CHANGED)) {
            undoButton.setEnabled((boolean) evt.getNewValue());
        }
        else if (evt.getPropertyName().equals(Player.Properties.COINS_AMOUNT_CHANGED)) {
            coinsLabel.setText("Gold Coins: " + FMT.format(this.player.getGoldCoins()));
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {
        if (!makingWord) {
            if (e.getKeyChar() == 's') {
                game.shift(ZEnd.Direction.DOWN);
            }
            else if (e.getKeyChar() == 'w') {
                game.shift(ZEnd.Direction.UP);
            }
            else if (e.getKeyChar() == 'd') {
                game.shift(ZEnd.Direction.RIGHT);
            }
            else if (e.getKeyChar() == 'a') {
                game.shift(ZEnd.Direction.LEFT);
            }
        }
    }

    @Override
    public void keyPressed(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    private String getHelpText() {
        return "To slide tiles:"
                + "\n\tw => UP"
                + "\n\ts => DOWN"
                + "\n\ta => LEFT"
                + "\n\td => RIGHT"
                + "\n\n"
                + "Z-End created by bryanProgramming"
                + "\nwww.bryanprogramming.com/ZEnd";
    }

    private JMenuBar buildMenu() {
        JMenuBar menuBar = new JMenuBar();
        JMenu file = new JMenu("File");

        JMenuItem item = new JMenuItem("New Game");
        item.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                game.startGame();
                resetboard();
            }
        });
        file.add(item);
        file.addSeparator();
        item = new JMenuItem("Purchase Coins");
        item.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                javax.swing.SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        CoinPurchaseFrame.createAndShowGUI(player);
                    }
                });
            }
        });

        file.add(item);
        item = new JMenuItem("Purchase Power Ups");
        item.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                javax.swing.SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        BuyPowerUpWindow.createAndShowGUI(player, game);
                    }
                });
            }
        });

        file.add(item);
        file.addSeparator();
        item = new JMenuItem("Exit");
        item.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        file.add(item);
        menuBar.add(file);

        JMenu help = new JMenu("Help");
        item = new JMenuItem("Z-End Help");
        item.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(null, getHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE);
            }
        });
        help.add(item);
        help.addSeparator();
        help.add(getTestCasesMenu());

        menuBar.add(help);
        return menuBar;
    }

    private JMenu getTestCasesMenu() {
        JMenu testCases = new JMenu("Test Cases:  ");

        Class testCaseClass = TestCases.class;
        Field[] cases = testCaseClass.getDeclaredFields();

        JMenuItem item;
        TestCases tc = new TestCases();
        for (Field f : cases) {
            try {
                item = new JMenuItem(f.getName());
                final char[][] testCase = (char[][]) f.get(tc);
                item.addActionListener(new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        game.startGame(testCase);
                        resetboard();
                    }
                });
                testCases.add(item);
            }
            catch (IllegalArgumentException ex) {

            }
            catch (IllegalAccessException ex) {

            }
        }

        return testCases;
    }
}
